| // Copyright (C) 2016 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package validate |
| |
| import ( |
| "android.googlesource.com/platform/tools/gpu/api/resolver" |
| "android.googlesource.com/platform/tools/gpu/api/semantic" |
| "android.googlesource.com/platform/tools/gpu/framework/parse" |
| ) |
| |
| type fieldUsage struct{ read, written bool } |
| |
| func baseType(t semantic.Type) semantic.Type { |
| for p, ok := t.(*semantic.Pseudonym); ok; p, ok = t.(*semantic.Pseudonym) { |
| t = p.To |
| } |
| return t |
| } |
| |
| const annoUnused = "unused" |
| |
| // noUnused verifies that all declared types and fields are used. |
| func noUnused(api *semantic.API, mappings *resolver.Mappings) Issues { |
| types := map[semantic.Type]bool{} |
| fields := map[*semantic.Field]fieldUsage{} |
| tokens := map[semantic.Node]parse.Token{} |
| |
| // Gather all declared types |
| for _, t := range api.Classes { |
| types[t] = false |
| tokens[t] = mappings.CST(t.AST).Token() |
| for _, f := range t.Fields { |
| // Embedded fields don't need validating |
| if _, class := baseType(f.Type).(*semantic.Class); !class { |
| fields[f] = fieldUsage{} |
| tokens[f] = mappings.CST(f.AST).Token() |
| } |
| } |
| } |
| for _, t := range api.Enums { |
| types[t] = false |
| tokens[t] = mappings.CST(t.AST).Token() |
| } |
| for _, t := range api.Pseudonyms { |
| types[t] = false |
| tokens[t] = mappings.CST(t.AST).Token() |
| } |
| |
| markFieldUsed := func(f *semantic.Field, read, written bool) { |
| if usage, ok := fields[f]; ok { |
| usage.written = usage.written || written |
| usage.read = usage.read || read |
| fields[f] = usage |
| } |
| } |
| |
| var markClassFieldsUsed func(ty semantic.Type, read, written bool) |
| markClassFieldsUsed = func(ty semantic.Type, read, written bool) { |
| switch ty := ty.(type) { |
| case *semantic.Class: |
| for _, f := range ty.Fields { |
| markFieldUsed(f, read, written) |
| } |
| case *semantic.Reference: |
| markClassFieldsUsed(ty.To, read, written) |
| case *semantic.Pseudonym: |
| markClassFieldsUsed(ty.To, read, written) |
| } |
| } |
| |
| // Functions for marking types as used |
| var markTypeUsed func(t semantic.Type) |
| markTypeUsed = func(t semantic.Type) { |
| if types[t] { |
| return |
| } |
| types[t] = true |
| switch t := t.(type) { |
| case *semantic.Reference: |
| markTypeUsed(t.To) |
| case *semantic.Slice: |
| markTypeUsed(t.To) |
| case *semantic.StaticArray: |
| markTypeUsed(t.ValueType) |
| case *semantic.Map: |
| markTypeUsed(t.ValueType) |
| markTypeUsed(t.KeyType) |
| case *semantic.Pointer: |
| markTypeUsed(t.To) |
| case *semantic.Pseudonym: |
| markTypeUsed(t.To) |
| case *semantic.Class: |
| for _, f := range t.Fields { |
| markTypeUsed(f.Type) |
| } |
| } |
| } |
| |
| var traverse func(n semantic.Node) |
| traverse = func(n semantic.Node) { |
| if e, ok := n.(semantic.Expression); ok { |
| markTypeUsed(e.ExpressionType()) |
| } |
| |
| switch n := n.(type) { |
| case *semantic.Assign: |
| if m, ok := n.LHS.(*semantic.Member); ok { |
| markFieldUsed(m.Field, false, true) |
| semantic.Visit(n.RHS, traverse) |
| return |
| } |
| case *semantic.ArrayAssign: |
| if m, ok := n.To.Array.(*semantic.Member); ok { |
| markFieldUsed(m.Field, false, true) |
| semantic.Visit(n.Value, traverse) |
| return |
| } |
| case *semantic.MapAssign: |
| if m, ok := n.To.Map.(*semantic.Member); ok { |
| markFieldUsed(m.Field, false, true) |
| semantic.Visit(n.Value, traverse) |
| return |
| } |
| case *semantic.Field: |
| markFieldUsed(n, true, false) |
| case *semantic.FieldInitializer: |
| markFieldUsed(n.Field, false, true) |
| return |
| case semantic.Type: |
| markTypeUsed(n) |
| return // Don't traverse into the type. |
| case *semantic.Callable: |
| return // Don't traverse into these. |
| } |
| semantic.Visit(n, traverse) |
| } |
| |
| // Traverse the API finding all used types |
| for _, g := range api.Globals { |
| types[g.Type] = true |
| } |
| for _, f := range api.Subroutines { |
| traverse(f) |
| } |
| for _, f := range api.Functions { |
| traverse(f) |
| } |
| for _, c := range api.Classes { |
| for _, m := range c.Methods { |
| traverse(m) |
| } |
| } |
| for _, c := range api.Pseudonyms { |
| for _, m := range c.Methods { |
| traverse(m) |
| } |
| } |
| for _, e := range api.Externs { |
| if e.Return != nil { |
| markClassFieldsUsed(e.Return.Type, false, true) |
| } |
| } |
| |
| // Report all types declared but not used as issues |
| issues := Issues{} |
| for t, used := range types { |
| if a, ok := t.(semantic.Annotated); ok { |
| anno := a.GetAnnotation(annoUnused) |
| if used && anno != nil { |
| issues.addf(mappings.CST(anno.AST), "Redundant annotation") |
| } |
| continue |
| } |
| if !used { |
| issues.addf(mappings.ParseNode(t), "Type %s declared but never used", t.Name()) |
| } |
| } |
| for f, usage := range fields { |
| var msg string |
| switch { |
| case !usage.read && usage.written: |
| msg = "Field %s.%s assigned but never read" |
| case usage.read && !usage.written: |
| msg = "Field %s.%s read but never assigned" |
| case !usage.read && !usage.written: |
| msg = "Field %s.%s never used" |
| } |
| class := f.Owner().(*semantic.Class) |
| unused := len(msg) > 0 |
| fiu, ciu := f.GetAnnotation(annoUnused), class.GetAnnotation(annoUnused) |
| if unused && fiu == nil && ciu == nil { |
| issues.addf(mappings.CST(f.AST), msg, f.Owner().Name(), f.Name()) |
| } |
| if !unused && fiu != nil && ciu == nil { |
| issues.addf(mappings.CST(fiu.AST), "Redundant annotation") |
| } |
| } |
| return issues |
| } |