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 10 commits
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
29 changes: 18 additions & 11 deletions runtime/sema/check_binary_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ import (

func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression) ast.Repr {

var leftType, rightType, resultType Type
defer func() {
elaboration := checker.Elaboration
elaboration.BinaryExpressionLeftTypes[expression] = leftType
elaboration.BinaryExpressionRightTypes[expression] = rightType
elaboration.BinaryExpressionResultTypes[expression] = resultType
}()

// The left-hand side is always evaluated.
// However, the right-hand side might not necessarily be evaluated,
// e.g. in boolean logic or in nil-coalescing
Expand Down Expand Up @@ -55,7 +63,7 @@ func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression)
// Visit the expression, with contextually expected type. Use the expected type
// only for inferring wherever possible, but do not check for compatibility.
// Compatibility is checked separately for each operand kind.
leftType := checker.VisitExpressionWithForceType(expression.Left, expectedType, false)
leftType = checker.VisitExpressionWithForceType(expression.Left, expectedType, false)

leftIsInvalid := leftType.IsInvalidType()

Expand Down Expand Up @@ -111,7 +119,7 @@ func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression)
expectedType = leftType
}

rightType := checker.VisitExpressionWithForceType(expression.Right, expectedType, false)
rightType = checker.VisitExpressionWithForceType(expression.Right, expectedType, false)

rightIsInvalid := rightType.IsInvalidType()

Expand All @@ -122,18 +130,20 @@ func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression)
BinaryOperationKindNonEqualityComparison,
BinaryOperationKindBitwise:

return checker.checkBinaryExpressionArithmeticOrNonEqualityComparisonOrBitwise(
resultType = checker.checkBinaryExpressionArithmeticOrNonEqualityComparisonOrBitwise(
expression, operation, operationKind,
leftType, rightType,
leftIsInvalid, rightIsInvalid, anyInvalid,
)
return resultType

case BinaryOperationKindEquality:
return checker.checkBinaryExpressionEquality(
resultType = checker.checkBinaryExpressionEquality(
expression, operation, operationKind,
leftType, rightType,
leftIsInvalid, rightIsInvalid, anyInvalid,
)
return resultType

default:
return unsupportedOperation()
Expand All @@ -146,7 +156,7 @@ func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression)
// That means that resource invalidation and returns
// are not definite, but only potential.

rightType := checker.checkPotentiallyUnevaluated(func() Type {
rightType = checker.checkPotentiallyUnevaluated(func() Type {
var expectedType Type
if !leftIsInvalid {
if optionalLeftType, ok := leftType.(*OptionalType); ok {
Expand All @@ -162,22 +172,19 @@ func (checker *Checker) VisitBinaryExpression(expression *ast.BinaryExpression)

switch operationKind {
case BinaryOperationKindBooleanLogic:
return checker.checkBinaryExpressionBooleanLogic(
resultType = checker.checkBinaryExpressionBooleanLogic(
expression, operation, operationKind,
leftType, rightType,
leftIsInvalid, rightIsInvalid, anyInvalid,
)
return resultType

case BinaryOperationKindNilCoalescing:
resultType := checker.checkBinaryExpressionNilCoalescing(
resultType = checker.checkBinaryExpressionNilCoalescing(
expression, operation, operationKind,
leftType, rightType,
leftIsInvalid, rightIsInvalid, anyInvalid,
)

checker.Elaboration.BinaryExpressionResultTypes[expression] = resultType
checker.Elaboration.BinaryExpressionRightTypes[expression] = rightType

return resultType

default:
Expand Down
4 changes: 3 additions & 1 deletion runtime/sema/check_conditional.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ func (checker *Checker) VisitIfStatement(statement *ast.IfStatement) ast.Repr {
func (checker *Checker) VisitConditionalExpression(expression *ast.ConditionalExpression) ast.Repr {

thenType, elseType := checker.visitConditional(expression.Test, expression.Then, expression.Else)

if thenType == nil || elseType == nil {
panic(errors.NewUnreachableError())
}
Expand Down Expand Up @@ -96,6 +95,9 @@ func (checker *Checker) VisitConditionalExpression(expression *ast.ConditionalEx
)
}

checker.Elaboration.ConditionalExpressionThenType[expression] = thenType
checker.Elaboration.ConditionalExpressionElseType[expression] = elseType

return resultType
}

Expand Down
6 changes: 6 additions & 0 deletions runtime/sema/elaboration.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Elaboration struct {
ReturnStatementValueTypes map[*ast.ReturnStatement]Type
ReturnStatementReturnTypes map[*ast.ReturnStatement]Type
BinaryExpressionResultTypes map[*ast.BinaryExpression]Type
BinaryExpressionLeftTypes map[*ast.BinaryExpression]Type
BinaryExpressionRightTypes map[*ast.BinaryExpression]Type
MemberExpressionMemberInfos map[*ast.MemberExpression]MemberInfo
MemberExpressionExpectedTypes map[*ast.MemberExpression]Type
Expand Down Expand Up @@ -90,6 +91,8 @@ type Elaboration struct {
IsResourceMoveIndexExpression map[*ast.IndexExpression]bool
IndexExpressionIndexedTypes map[*ast.IndexExpression]ValueIndexableType
IndexExpressionIndexingTypes map[*ast.IndexExpression]Type
ConditionalExpressionThenType map[*ast.ConditionalExpression]Type
ConditionalExpressionElseType map[*ast.ConditionalExpression]Type
}

func NewElaboration() *Elaboration {
Expand Down Expand Up @@ -117,6 +120,7 @@ func NewElaboration() *Elaboration {
ReturnStatementValueTypes: map[*ast.ReturnStatement]Type{},
ReturnStatementReturnTypes: map[*ast.ReturnStatement]Type{},
BinaryExpressionResultTypes: map[*ast.BinaryExpression]Type{},
BinaryExpressionLeftTypes: map[*ast.BinaryExpression]Type{},
BinaryExpressionRightTypes: map[*ast.BinaryExpression]Type{},
MemberExpressionMemberInfos: map[*ast.MemberExpression]MemberInfo{},
MemberExpressionExpectedTypes: map[*ast.MemberExpression]Type{},
Expand Down Expand Up @@ -145,6 +149,8 @@ func NewElaboration() *Elaboration {
ReferenceExpressionBorrowTypes: map[*ast.ReferenceExpression]*ReferenceType{},
IndexExpressionIndexedTypes: map[*ast.IndexExpression]ValueIndexableType{},
IndexExpressionIndexingTypes: map[*ast.IndexExpression]Type{},
ConditionalExpressionThenType: map[*ast.ConditionalExpression]Type{},
ConditionalExpressionElseType: map[*ast.ConditionalExpression]Type{},
}
}

Expand Down
46 changes: 46 additions & 0 deletions tools/analysis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
package analysis

import (
"fmt"

"github.com/onflow/cadence/runtime/ast"
"github.com/onflow/cadence/runtime/common"
)
Expand All @@ -40,3 +42,47 @@ type Config struct {
importRange ast.Range,
) (string, error)
}

func NewSimpleConfig(
mode LoadMode,
codes map[common.LocationID]string,
contractNames map[common.Address][]string,
) *Config {
config := &Config{
Mode: mode,
ResolveAddressContractNames: func(
address common.Address,
) (
[]string,
error,
) {
names, ok := contractNames[address]
if !ok {
return nil, fmt.Errorf(
"missing contracts for address: %s",
address,
)
}
return names, nil
},
ResolveCode: func(
location common.Location,
importingLocation common.Location,
importRange ast.Range,
) (
string,
error,
) {
code, ok := codes[location.ID()]
if !ok {
return "", fmt.Errorf(
"import of unknown location: %s",
location,
)
}

return code, nil
},
}
return config
}
1 change: 1 addition & 0 deletions tools/analysis/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

type Program struct {
Location common.Location
Code string
Program *ast.Program
Elaboration *sema.Elaboration
}
Expand Down
1 change: 1 addition & 0 deletions tools/analysis/programs.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func (programs Programs) load(

programs[location.ID()] = &Program{
Location: location,
Code: code,
Program: program,
Elaboration: elaboration,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* limitations under the License.
*/

package main
package analyzers

import (
"fmt"
Expand All @@ -25,12 +25,12 @@ import (
"github.com/onflow/cadence/tools/analysis"
)

var analyzers = map[string]*analysis.Analyzer{}
var Analyzers = map[string]*analysis.Analyzer{}

var analyzerNamePattern = regexp.MustCompile(`\w+`)

func registerAnalyzer(name string, analyzer *analysis.Analyzer) {
if _, ok := analyzers[name]; ok {
if _, ok := Analyzers[name]; ok {
panic(fmt.Errorf("analyzer already exists: %s", name))
}

Expand All @@ -39,5 +39,5 @@ func registerAnalyzer(name string, analyzer *analysis.Analyzer) {

}

analyzers[name] = analyzer
Analyzers[name] = analyzer
}
Loading