-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix detection of subexpressions in :S and :C modifiers
- Loading branch information
Showing
4 changed files
with
243 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package pkglint | ||
|
||
// MkWalker walks through a makefile line or a text snippet from such a line, | ||
// visiting the expressions and their subexpressions. | ||
type MkWalker struct { | ||
Diag Autofixer | ||
Expr func(expr *MkExpr, time EctxTime) | ||
} | ||
|
||
func NewMkWalker(diag Autofixer, expr func(expr *MkExpr, time EctxTime)) *MkWalker { | ||
return &MkWalker{diag, expr} | ||
} | ||
|
||
// WalkLine calls the action for each variable that is used in the line. | ||
func (w *MkWalker) WalkLine(mkline *MkLine) { | ||
switch { | ||
|
||
case mkline.IsVarassign(): | ||
w.WalkText(mkline.Varname(), EctxLoadTime) | ||
w.WalkText(mkline.Value(), mkline.Op().Time()) | ||
|
||
case mkline.IsDirective(): | ||
w.walkDirective(mkline) | ||
|
||
case mkline.IsShellCommand(): | ||
w.WalkText(mkline.ShellCommand(), EctxRunTime) | ||
|
||
case mkline.IsDependency(): | ||
w.WalkText(mkline.Targets(), EctxLoadTime) | ||
w.WalkText(mkline.Sources(), EctxLoadTime) | ||
|
||
case mkline.IsInclude(): | ||
w.WalkText(mkline.IncludedFile().String(), EctxLoadTime) | ||
} | ||
} | ||
|
||
func (w *MkWalker) WalkText(text string, time EctxTime) { | ||
if !contains(text, "$") { | ||
return | ||
} | ||
|
||
tokens, _ := NewMkLexer(text, nil).MkTokens() | ||
for _, token := range tokens { | ||
if token.Expr != nil { | ||
w.walkExpr(token.Expr, time) | ||
} | ||
} | ||
} | ||
|
||
func (w *MkWalker) walkDirective(mkline *MkLine) { | ||
switch mkline.Directive() { | ||
case "error", "for", "info", "warning": | ||
w.WalkText(mkline.Args(), EctxLoadTime) | ||
case "if", "elif": | ||
if cond := mkline.Cond(); cond != nil { | ||
cond.Walk(&MkCondCallback{ | ||
Expr: func(expr *MkExpr) { | ||
w.walkExpr(expr, EctxLoadTime) | ||
}}) | ||
} | ||
} | ||
} | ||
|
||
func (w *MkWalker) walkExpr(expr *MkExpr, time EctxTime) { | ||
varname := expr.varname | ||
if !expr.IsExpression() { | ||
w.Expr(expr, time) | ||
} | ||
w.WalkText(varname, time) | ||
for _, mod := range expr.modifiers { | ||
w.walkModifier(mod, time) | ||
} | ||
} | ||
|
||
func (w *MkWalker) walkModifier(mod MkExprModifier, time EctxTime) { | ||
if !contains(mod.String(), "$") { | ||
return | ||
} | ||
if mod.HasPrefix("@") { | ||
// XXX: Probably close enough for most practical cases. | ||
// If not, implement bmake's ParseModifierPartBalanced. | ||
w.WalkText(mod.String(), time) | ||
return | ||
} | ||
if mod.HasPrefix("?") || mod.HasPrefix("D") || mod.HasPrefix("U") || mod.HasPrefix("!") { | ||
// XXX: Probably close enough, but \$ and $$ differ. | ||
w.WalkText(mod.String(), time) | ||
return | ||
} | ||
if ok, _, from, to, _ := mod.MatchSubst(); ok { | ||
// XXX: Probably close enough, but \$ and $$ differ. | ||
w.WalkText(from, time) | ||
// XXX: Probably close enough, but \$ and $$ differ. | ||
w.WalkText(to, time) | ||
return | ||
} | ||
if ok, _, pattern, _ := mod.MatchMatch(); ok { | ||
// XXX: Probably close enough, but \$ and $$ differ. | ||
w.WalkText(pattern, time) | ||
return | ||
} | ||
w.Diag.Errorf("Modifier \"%s\" not implemented.", mod.String()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package pkglint | ||
|
||
import "gopkg.in/check.v1" | ||
|
||
func (s *Suite) Test_NewMkWalker(c *check.C) { | ||
t := s.Init(c) | ||
|
||
mkline := t.NewMkLine("filename.mk", 123, "LEFT.$p=\t${RIGHT}") | ||
action := func(expr *MkExpr, time EctxTime) { | ||
mkline.Notef("Expression \"%s\" at \"%s\" time.", expr.String(), time.String()) | ||
} | ||
|
||
walker := NewMkWalker(mkline, action) | ||
walker.WalkLine(mkline) | ||
|
||
t.CheckOutputLines( | ||
"WARN: filename.mk:123: $p is ambiguous. Use ${p} if you mean a Make variable or $$p if you mean a shell variable.", | ||
"NOTE: filename.mk:123: Expression \"${p}\" at \"load\" time.", | ||
"NOTE: filename.mk:123: Expression \"${RIGHT}\" at \"run\" time.") | ||
} | ||
|
||
func (s *Suite) Test_MkWalker_WalkLine(c *check.C) { | ||
t := s.Init(c) | ||
|
||
mklines := t.NewMkLines("filename.mk", | ||
MkCvsID, | ||
"LEFT.$p=\t${RIGHT}", | ||
".for i in ${LEFT.$p}", | ||
".if ${left}", | ||
"\t${COMMAND}", | ||
"${TARGET}: ${SOURCE}", | ||
".include \"${DIR}/Makefile\"") | ||
|
||
mklines.ForEach(func(mkline *MkLine) { | ||
action := func(expr *MkExpr, time EctxTime) { | ||
mkline.Notef("Expression \"%s\" at \"%s\" time.", expr.String(), time.String()) | ||
} | ||
walker := NewMkWalker(mkline, action) | ||
walker.WalkLine(mkline) | ||
}) | ||
|
||
t.CheckOutputLines( | ||
"WARN: filename.mk:2: $p is ambiguous. Use ${p} if you mean a Make variable or $$p if you mean a shell variable.", | ||
"NOTE: filename.mk:2: Expression \"${p}\" at \"load\" time.", | ||
"NOTE: filename.mk:2: Expression \"${RIGHT}\" at \"run\" time.", | ||
"NOTE: filename.mk:3: Expression \"${LEFT.$p}\" at \"load\" time.", | ||
"NOTE: filename.mk:3: Expression \"${p}\" at \"load\" time.", | ||
"NOTE: filename.mk:4: Expression \"${left}\" at \"load\" time.", | ||
"NOTE: filename.mk:5: Expression \"${COMMAND}\" at \"run\" time.", | ||
"NOTE: filename.mk:6: Expression \"${TARGET}\" at \"load\" time.", | ||
"NOTE: filename.mk:6: Expression \"${SOURCE}\" at \"load\" time.", | ||
"NOTE: filename.mk:7: Expression \"${DIR}\" at \"load\" time.") | ||
} | ||
|
||
func (s *Suite) Test_MkWalker_WalkText(c *check.C) { | ||
t := s.Init(c) | ||
|
||
mklines := t.NewMkLines("filename.mk", | ||
MkCvsID, | ||
"\t${COMMAND}") | ||
|
||
mklines.ForEach(func(mkline *MkLine) { | ||
action := func(expr *MkExpr, time EctxTime) { | ||
mkline.Notef("Expression \"%s\" at \"%s\" time.", expr.String(), time.String()) | ||
} | ||
walker := NewMkWalker(mkline, action) | ||
walker.WalkLine(mkline) | ||
}) | ||
|
||
t.CheckOutputLines( | ||
"NOTE: filename.mk:2: Expression \"${COMMAND}\" at \"run\" time.") | ||
} | ||
|
||
func (s *Suite) Test_MkWalker_walkDirective(c *check.C) { | ||
t := s.Init(c) | ||
|
||
mklines := t.NewMkLines("filename.mk", | ||
".info ${INFO:S<from$<to<}", | ||
".if ${COND:S<from$<to<}", | ||
".endif") | ||
|
||
mklines.ForEach(func(mkline *MkLine) { | ||
action := func(expr *MkExpr, time EctxTime) { | ||
mkline.Notef("Expression \"%s\" at \"%s\" time.", expr.String(), time.String()) | ||
} | ||
walker := NewMkWalker(mkline, action) | ||
walker.walkDirective(mkline) | ||
}) | ||
|
||
t.CheckOutputLines( | ||
"NOTE: filename.mk:1: Expression \"${INFO:S<from$<to<}\" at \"load\" time.", | ||
"NOTE: filename.mk:2: Expression \"${COND:S<from$<to<}\" at \"load\" time.") | ||
} | ||
|
||
func (s *Suite) Test_MkWalker_walkExpr(c *check.C) { | ||
t := s.Init(c) | ||
|
||
mklines := t.NewMkLines("filename.mk", | ||
"${LEFT:S<from$<to<}=\t${RIGHT:S<from$<to<}") | ||
|
||
mklines.ForEach(func(mkline *MkLine) { | ||
action := func(expr *MkExpr, time EctxTime) { | ||
mkline.Notef("Expression \"%s\" at \"%s\" time.", expr.String(), time.String()) | ||
} | ||
walker := NewMkWalker(mkline, action) | ||
walker.WalkLine(mkline) | ||
}) | ||
|
||
t.CheckOutputLines( | ||
"NOTE: filename.mk:1: Expression \"${LEFT:S<from$<to<}\" at \"load\" time.", | ||
"NOTE: filename.mk:1: Expression \"${RIGHT:S<from$<to<}\" at \"run\" time.") | ||
} | ||
|
||
func (s *Suite) Test_MkWalker_walkModifier(c *check.C) { | ||
t := s.Init(c) | ||
|
||
mklines := t.NewMkLines("filename.mk", | ||
"MOD.S=\t${VAR:S<from$<to<}", | ||
"MOD.S=\t${VAR:S<${from}$<${to}<}", | ||
) | ||
|
||
mklines.ForEach(func(mkline *MkLine) { | ||
action := func(expr *MkExpr, time EctxTime) { | ||
mkline.Notef("Expression \"%s\" at \"%s\" time.", expr.String(), time.String()) | ||
} | ||
walker := NewMkWalker(mkline, action) | ||
walker.WalkLine(mkline) | ||
}) | ||
|
||
t.CheckOutputLines( | ||
"NOTE: filename.mk:1: Expression \"${VAR:S<from$<to<}\" at \"run\" time.", | ||
"NOTE: filename.mk:2: Expression \"${VAR:S<${from}$<${to}<}\" at \"run\" time.", | ||
"NOTE: filename.mk:2: Expression \"${from}\" at \"run\" time.", | ||
"NOTE: filename.mk:2: Expression \"${to}\" at \"run\" time.") | ||
} |